package Utils.word;

import Utils.data.StringUtils;
import Utils.word.dataTypes.ProcessResult;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 标记命令解析器
 *
 * @author d11yu
 */
public class MarkerCommandParser {
    //region 常量
    /**
     * 单标签匹配规则，默认标签：${XXX}
     */
    public static final String MARKER1_REGEX = "\\$\\{[^/}][^\\{}]*[^/]}";
    /**
     * 单标签头匹配规则
     */
    public static final String MARKER1_HEADER_REGEX = "(?<=\\$\\{\\s*)[^\\s/]+(?=\\s*[^(/})]*})";
//    public static final String MARKER1_HEADER_REGEX = "head";
    /**
     * 单标签内容匹配规则
     */
    public static final String MARKER1_CONTENT_REGEX = "(?<=\\$\\{)[^/}][^\\{}]*[^/](?=})";
    /**
     * 单标签命令字符串匹配规则
     */
    public static final String MARKER1_HEAD_COMMAND_REGEX = "(?<=\\$\\{\\s*\\S+\\s+)\\S+[\\s\\S]+(?=})";
    /**
     * 双标签头标签匹配规则
     */
    public static final String MARKER2_HEAD_REGEX = "\\$\\{[^/]+[^\\{\\}]*/\\}";
    /**
     * 双标签头标签内容匹配规则
     */
    public static final String MARKER2_HEAD_CONTENT_REGEX = "(?<=\\$\\{)[^/]+[^\\{\\}]*(?=/\\})";
    /**
     * 双标签头标签头部匹配规则
     */
    public static final String MARKER2_HEAD_HEADER_REGEX = "(?<=\\$\\{\\s*)[^\\s]+(?=\\s*[^(/})]*/})";
    /**
     * 双标签头标签命令字符串匹配规则
     */
    public static final String MARKER2_HEAD_COMMAND_REGEX = "(?<=\\$\\{\\s*\\S+\\s+)\\S+[\\s\\S]+(?=\\s*/})";
    /**
     * 双标签头标签的左边框
     */
    public static final String MARKER2_HEAD_LEFT = "${";
    /**
     * 双标签头标签的右边框
     */
    public static final String MARKER2_HEAD_RIGHT = "/}";
    /**
     * 双标签尾标签的左边框
     */
    public static final String MARKER2_TAIL_LEFT = "${/";
    /**
     * 双标签尾标签的右边框
     */
    public static final String MARKER2_TAIL_RIGHT = "}";
    //endregion

    /**
     * 判断IBodyElement列表是否为空（即存在easypoi的标签未被填充），只要有一个为空，即整体为空。
     *
     * @param eles
     * @return
     */
    public static Boolean isBodyElementsEmpty(List<IBodyElement> eles) {
        if (eles == null || eles.size() == 0) {
            return true;
        }
        for (IBodyElement ele : eles) {
            Boolean isEmpty = isBodyElementEmpty(ele);
            if (isEmpty) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通过双坐标头部获取双坐标头。
     *
     * @param header
     * @return
     */
    public static String getMarker2HeadRegexWithHeader(String header) {
        return "\\$\\{\\s*" + header + "[^}]*/}";
    }

    /**
     * 通过双坐标头部获取双坐标头。
     *
     * @param header
     * @return
     */
    public static String getMarker2TailRegexWithHeader(String header) {
        return "\\$\\{/\\s*" + header + "[^}]*}";
    }

    /**
     * 通过双坐标头获取双坐标尾。
     *
     * @param header
     * @return
     */
    public static String getMarker2Tail(String header) {
        return MARKER2_TAIL_LEFT + header + MARKER2_TAIL_RIGHT;
    }

    /**
     * 判断IBodyElement是否为空（即存在easypoi的标签未被填充）。
     *
     * @param ele
     * @return
     */
    public static Boolean isBodyElementEmpty(IBodyElement ele) {
        if (ele == null) {
            return true;
        }
        String text = PoiUtils.getBodyElementText(ele);
        if (text != null) {
            return text.contains("{{");
        } else {
            return true;
        }
    }

    /**
     * 从双标签的头标签获取标签内容，如果未匹配到，则返回null。
     *
     * @param marker
     * @return 如果未匹配到，则返回null。
     */
    public static String getContent(String marker) {
        return StringUtils.getFirstMatch(marker, MarkerCommandParser.MARKER2_HEAD_CONTENT_REGEX);
    }

    /**
     * 从双标签的头标签获取标签头，如果未匹配到，则返回null。
     *
     * @param marker
     * @return 如果未匹配到，则返回null。
     */
    public static String getHeader(String marker) {
        return StringUtils.getFirstMatch(marker, MarkerCommandParser.MARKER2_HEAD_HEADER_REGEX);
    }

    /**
     * 从双标签的头标签获取命令字符串，如果未匹配到，则返回null。
     *
     * @param marker
     * @return
     */
    public static String getCommandTwoMarker(String marker) {
        String s = StringUtils.getFirstMatch(marker, MarkerCommandParser.MARKER2_HEAD_COMMAND_REGEX);
        if (s == null) {
            return null;
        }

        return s.trim();
    }

    /**
     * 从单标签获取命令字符串，如果未匹配到，则返回null。
     *
     * @param marker
     * @return
     */
    public static String getCommandOneMarker(String marker) {
        String s = StringUtils.getFirstMatch(marker, MarkerCommandParser.MARKER1_HEAD_COMMAND_REGEX);
        if (s == null) {
            return null;
        }

        return s.trim();
    }

    /**
     * 判断是否要删除标签
     *
     * @param headMarkerPara
     */
    public static ProcessResult process(XWPFParagraph headMarkerPara) {
        XWPFDocument doc = headMarkerPara.getDocument();
        String command = getCommandTwoMarker(headMarkerPara.getText());
        if (command != null && !"".equals(command)) {
            // 如果是双标签
            List<String> s = trimCommand(command);
            String header = getHeader(headMarkerPara.getText());
            String headMarker = getMarker2HeadRegexWithHeader(header);
            int posOfPara = doc.getPosOfParagraph(headMarkerPara);
            List<IBodyElement> elements = PoiUtils.locateBodyElementsByTwoMarkers(doc, headMarker, getMarker2TailRegexWithHeader(header), posOfPara, false);
            if (elements == null) {
                return ProcessResult.DID_NOTHING;
            }

            // delete if null
            if ("null".equals(s.get(2))) {
                if (isBodyElementsEmpty(elements)) {
                    PoiUtils.deleteBodyElements(elements);
                    return ProcessResult.DELETED_CONTENT;
                }
                PoiUtils.deleteBodyElement(elements.get(0));
                PoiUtils.deleteBodyElement(elements.get(elements.size() - 1));
                return ProcessResult.DELETED_MARKERS;
            }

            // delete if no table
            if ("no".equals(s.get(2)) && "table".equals(s.get(3))) {
                boolean noTable = true;
                for (IBodyElement element : elements) {
                    if (element instanceof XWPFTable) {
                        noTable = false;
                        break;
                    }
                }
                if (noTable) {
                    PoiUtils.deleteBodyElements(elements);
                    return ProcessResult.DELETED_CONTENT;
                }
                return ProcessResult.DID_NOTHING;
            }

            // delete if 表 ！= null 或 delete if 表 == null
            String header1 = s.get(2);
            List<IBodyElement> elements1 = PoiUtils.locateBodyElementsByTwoMarkers(doc, getMarker2HeadRegexWithHeader(header1), getMarker2TailRegexWithHeader(header1), 0, false);
            if (elements1 == null) {
                // 如果目标标签不存在，则视为空
                if ("==".equals(s.get(3))) {
                    PoiUtils.deleteBodyElements(elements);
                    return ProcessResult.DELETED_CONTENT;
                }
            }

            // delete if 表 ！= null
            if ("!=".equals(s.get(3))) {
                if (!isBodyElementsEmpty(elements1)) {
                    PoiUtils.deleteBodyElements(elements);
                    return ProcessResult.DELETED_CONTENT;
                }
            }

            // delete if 表 == null
            if ("==".equals(s.get(3))) {
                if (isBodyElementsEmpty(elements1)) {
                    PoiUtils.deleteBodyElements(elements);
                    return ProcessResult.DELETED_CONTENT;
                }
            }

            // 删除标签头尾
            PoiUtils.deleteBodyElement(elements.get(0));
            PoiUtils.deleteBodyElement(elements.get(elements.size() - 1));
            return ProcessResult.DELETED_MARKERS;

        } else {
            // 如果是单标签
            command = getCommandOneMarker(headMarkerPara.getText());
            if (command == null || "".equals(command)) {
                return ProcessResult.DID_NOTHING;
            }
            List<String> s = trimCommand(command);

            // delete if null 在单标签中不能这样写。
            // delete if no table在单标签中不能这样写。

            // delete if 表 ！= null 或 delete if 表 == null
            String header1 = s.get(2);
            List<IBodyElement> elements1 = PoiUtils.locateBodyElementsByTwoMarkers(doc, getMarker2HeadRegexWithHeader(header1), getMarker2TailRegexWithHeader(header1), 0, false);
            if (elements1 == null) {
                // 如果目标标签不存在，则视为空
                if ("==".equals(s.get(3))) {
                    PoiUtils.deleteBodyElement(headMarkerPara);
                    return ProcessResult.DELETED_CONTENT;
                }
            }

            // delete if 表 ！= null
            if ("!=".equals(s.get(3))) {
                if (!isBodyElementsEmpty(elements1)) {
                    PoiUtils.deleteBodyElement(headMarkerPara);
                    return ProcessResult.DELETED_CONTENT;
                }
            }

            // delete if 表 == null
            if ("==".equals(s.get(3))) {
                if (isBodyElementsEmpty(elements1)) {
                    PoiUtils.deleteBodyElement(headMarkerPara);
                    return ProcessResult.DELETED_CONTENT;
                }
            }
        }
        return ProcessResult.DID_NOTHING;
    }

    private static List<String> trimCommand(String command) {
        List<String> s = new ArrayList<>(Arrays.asList(command.split(" ")));
        List<String> s1 = new ArrayList<>();
        for (int i = 0; i < s.size(); i++) {
            s.set(i, s.get(i).trim());
            if ("".equals(s.get(i))) {
                s1.add(s.get(i));
            }
        }
        s.removeAll(s1);

        int size = s.size();
        for (int i = size - 1; i > 0; i--) {
            String str = s.get(i);
            if (str.contains("==") && !"==".equals(str)) {
                s.add(i, str.substring(str.indexOf("==") + 2));
                s.add(i, "==");
                s.add(i, str.substring(0, str.indexOf("==") - 1));
                s.remove(str);
            }
            if (str.contains("!=") && !"!=".equals(str)) {
                s.add(i, str.substring(str.indexOf("!=") + 2));
                s.add(i, "!=");
                s.add(i, str.substring(0, str.indexOf("!=") - 1));
                s.remove(str);
            }
        }
        return s;
    }
}
